package com.javabi.common.concurrent.collect.readwrite;

import static java.util.Collections.unmodifiableCollection;
import static java.util.Collections.unmodifiableSet;

import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import com.google.common.collect.ForwardingMap;

public abstract class ReadWriteForwardingMap<K, V> extends ForwardingMap<K, V> {

	private final transient ReadWriteLock lock;
	private final transient Lock readLock;
	private final transient Lock writeLock;

	protected ReadWriteForwardingMap(boolean fair) {
		this.lock = new ReentrantReadWriteLock(fair);
		this.readLock = lock.readLock();
		this.writeLock = lock.writeLock();
	}

	protected ReadWriteForwardingMap() {
		// Default to fair locking ...
		// write locks must be able to lock in a timely manner
		this(true);
	}

	public Lock getReadLock() {
		return readLock;
	}

	public Lock getWriteLock() {
		return writeLock;
	}

	@Override
	public int size() {
		Lock lock = getReadLock();
		lock.lock();
		try {
			return super.size();
		} finally {
			lock.unlock();
		}
	}

	@Override
	public boolean isEmpty() {
		Lock lock = getReadLock();
		lock.lock();
		try {
			return super.isEmpty();
		} finally {
			lock.unlock();
		}
	}

	@Override
	public boolean containsKey(Object key) {
		Lock lock = getReadLock();
		lock.lock();
		try {
			return super.containsKey(key);
		} finally {
			lock.unlock();
		}
	}

	@Override
	public boolean containsValue(Object value) {
		Lock lock = getReadLock();
		lock.lock();
		try {
			return super.containsValue(value);
		} finally {
			lock.unlock();
		}
	}

	@Override
	public V get(Object key) {
		Lock lock = getReadLock();
		lock.lock();
		try {
			return super.get(key);
		} finally {
			lock.unlock();
		}
	}

	@Override
	public V put(K key, V value) {
		Lock lock = getWriteLock();
		lock.lock();
		try {
			return super.put(key, value);
		} finally {
			lock.unlock();
		}
	}

	@Override
	public V remove(Object key) {
		Lock lock = getWriteLock();
		lock.lock();
		try {
			return super.remove(key);
		} finally {
			lock.unlock();
		}
	}

	@Override
	public void putAll(Map<? extends K, ? extends V> map) {
		Lock lock = getWriteLock();
		lock.lock();
		try {
			super.putAll(map);
		} finally {
			lock.unlock();
		}
	}

	@Override
	public void clear() {
		Lock lock = getWriteLock();
		lock.lock();
		try {
			super.clear();
		} finally {
			lock.unlock();
		}
	}

	@Override
	public Set<K> keySet() {
		Lock lock = getReadLock();
		lock.lock();
		try {
			return unmodifiableSet(super.keySet());
		} finally {
			lock.unlock();
		}
	}

	@Override
	public Collection<V> values() {
		Lock lock = getReadLock();
		lock.lock();
		try {
			return unmodifiableCollection(super.values());
		} finally {
			lock.unlock();
		}
	}

	@Override
	public Set<Entry<K, V>> entrySet() {
		Lock lock = getReadLock();
		lock.lock();
		try {
			return unmodifiableSet(super.entrySet());
		} finally {
			lock.unlock();
		}
	}

}
